package jnpf.base.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jnpf.base.ActionResult;
import jnpf.base.model.dbsync.DbSyncForm;
import jnpf.base.model.dbsync.DbSyncVo;
import jnpf.base.service.DbLinkService;
import jnpf.base.service.DbSyncService;
import jnpf.base.service.DbTableService;
import jnpf.database.datatype.db.interfaces.DtInterface;
import jnpf.database.datatype.sync.util.DtSyncUtil;
import jnpf.database.util.ConnUtil;
import jnpf.database.util.DbTypeUtil;
import jnpf.exception.DataException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/**
 * 数据同步
 *
 * @author JNPF开发平台组
 * @version V3.1.0
 * @copyright 引迈信息技术有限公司
 * @date 2019年9月27日 上午9:18
 */
@Api(tags = "数据同步", value = "DataSync")
@RestController
@RequestMapping("/api/system/DataSync")
public class DbSyncController {

    @Autowired
    private DbSyncService dbSyncService;
    @Autowired
    private DbLinkService dblinkService;
    @Autowired
    private DbTableService dbTableService;

    @PostMapping("Actions/checkDbLink")
    @ApiOperation("验证连接")
    public ActionResult<DbSyncVo> checkDbLink(@RequestBody DbSyncForm dbSyncForm) throws Exception {
        String fromDbType;
        String toDbType;
        try {
            fromDbType = DbTypeUtil.getDb(ConnUtil.getConn(dblinkService.getInfo(dbSyncForm.getDbConnectionFrom()))).getJnpfDbEncode();
        }catch (DataException e){
            throw new DataException("数据连接From连接失败:" + e.getMessage());
        }
        try {
            toDbType = DbTypeUtil.getDb(ConnUtil.getConn(dblinkService.getInfo(dbSyncForm.getDbConnectionTo()))).getJnpfDbEncode();
        }catch (DataException e){
            throw new DataException("数据连接To连接失败:" + e.getMessage());
        }
        DbSyncVo vo = new DbSyncVo();
        vo.setCheckDbFlag(true);
        vo.setTableList(dbTableService.getList(dbSyncForm.getDbConnectionFrom()));
        // 字段类型全部对应关系
        Map<String, List<String>> ruleMap = getConvertRules(fromDbType, toDbType).getData();
        Map<String, String> defaultRuleMap = getDefaultRules(fromDbType, toDbType).getData();
        // 默认类型置顶
        for (String key : defaultRuleMap.keySet()) {
            List<String> list = ruleMap.get(key);
            if(list != null){
                String rule = defaultRuleMap.get(key);
                list.remove(rule);
                list.add(0, rule + " (默认)");
                ruleMap.put(key, list);
            }
        }
        vo.setConvertRuleMap(ruleMap);
        return ActionResult.success(vo);
    }

    /**
     * 执行数据同步
     *
     * @param dbSyncForm 数据同步参数
     * @return ignore
     * @throws Exception ignore
     */
    @PostMapping
    @ApiOperation("数据同步校验")
    public ActionResult<Object> checkExecute(@RequestBody DbSyncForm dbSyncForm) throws Exception {
        int status;
        try {
            status = dbSyncService.checkExecute(dbSyncForm.getDbConnectionFrom(), dbSyncForm.getDbConnectionTo(), dbSyncForm.getConvertRuleMap(), dbSyncForm.getDbTable());
        } catch (Exception e) {
            e.printStackTrace();
            return ActionResult.fail(e.getMessage());
        }
        if (status == -1) {
            return ActionResult.fail("请检查，同一数据库下无法同步数据");
        }
        return ActionResult.success(status);
    }

    /**
     * 执行数据同步
     *
     * @param dbSyncForm 数据同步参数
     * @return ignore
     * @throws Exception ignore
     */
    @PostMapping("Actions/Execute")
    @ApiOperation("执行数据同步")
    public ActionResult<String> execute(@RequestBody DbSyncForm dbSyncForm) {
        try{
            dbSyncService.executeImport(dbSyncForm.getDbConnectionFrom(), dbSyncForm.getDbConnectionTo(), dbSyncForm.getConvertRuleMap(), dbSyncForm.getDbTable());
        }catch (Exception e){
            e.printStackTrace();
            return ActionResult.fail("同步失败：" + e.getMessage());
        }
        return ActionResult.success("成功");
    }


    /**
     * 批量执行数据同步
     *
     * @param dbSyncForm 数据同步参数
     * @return ignore
     * @throws Exception ignore
     */
    @PostMapping("Actions/batchExecute")
    @ApiOperation("批量执行数据同步")
    public ActionResult<Map<String, Integer>> batchExecute(@RequestBody DbSyncForm dbSyncForm) throws Exception {
        Map<String, Integer> result = dbSyncService.batchExecuteImport(dbSyncForm.getDbConnectionFrom(), dbSyncForm.getDbConnectionTo(), dbSyncForm.getConvertRuleMap(), dbSyncForm.getDbTableList());
        boolean flag = true;
        List<String> failTableList = new ArrayList<>();
        for (String table : result.keySet()) {
            if(result.get(table) == 0){
                failTableList.add(table);
                flag = false;
            }
        }
        if(flag){
            return ActionResult.success("1", result);
        }else {
            return ActionResult.success("以下表执行同步失败: " + String.join(",", failTableList), result);
        }
    }




    /**
     * 获取数据类型默认转换规则
     * 一对一
     * @param fromDbType 被转换数据库类型
     * @param toDbType 转换数据库类型
     * @return 转换规则
     * @throws Exception 未找到数库
     */
    @GetMapping("Actions/getDefaultRules")
    @ApiOperation("获取一对一数据类型默认转换规则")
    public static ActionResult<Map<String, String>> getDefaultRules(String fromDbType, String toDbType) throws Exception{
        Map<String, String> map = new LinkedHashMap<>();
        for (DtInterface dtInterface : DtInterface.getClz(fromDbType).getEnumConstants()) {
            DtInterface toFixCovert = DtSyncUtil.getToFixCovert(dtInterface, toDbType);
            if(toFixCovert != null){
                map.put(dtInterface.getDataType(), toFixCovert.getDataType());
            }
        }
        return ActionResult.success(map);
    }

    /**
     * 获取数据类型转换规则
     * 一对多
     * @param fromDbType 被转换数据库类型
     * @param toDbType 转换数据库类型
     * @return 转换规则
     * @throws Exception 未找到数库
     */
    @GetMapping("Actions/getConvertRules")
    @ApiOperation("获取一对多数据类型转换规则")
    public static ActionResult<Map<String, List<String>>> getConvertRules(String fromDbType, String toDbType) throws Exception{
        Map<String, List<String>> map = new LinkedHashMap<>();
        for (DtInterface dtInterface : DtInterface.getClz(fromDbType).getEnumConstants()) {
            List<String> list = new LinkedList<>();
            DtInterface[] allConverts = DtSyncUtil.getAllConverts(dtInterface, toDbType);
            if(allConverts != null){
                for (DtInterface allConvert : allConverts) {
                    list.add(allConvert.getDataType());
                }
                map.put(dtInterface.getDataType(), list);
            }
        }
        return ActionResult.success(map);
    }

}
